package Proofs;

import java.io.File;

import Verifier.GeneralUtil;

import ArithmeticLibrary.Elements.ElementGroup;
import ArithmeticLibrary.Elements.ElementProductField;
import ArithmeticLibrary.Elements.ElementProductGroup;
import ArithmeticLibrary.Sets.Field;
import ArithmeticLibrary.Sets.Group;
import ArithmeticLibrary.Sets.PrimeField;
import ArithmeticLibrary.Sets.ProductField;
import ArithmeticLibrary.Sets.ProductGroup;
import ByteTree.ByteTree;

/**
 * This class verify the consistenty of keys.
 * @author Hadar Miller and Or Stern
 *
 */
public class VerifyKeys {

	/** 
	 * This function reads the joint public key, the individual public keys, 
	 * and any recovered secret keys, and then verifies that they are all consistent. 
	 * 
	 * @param lambda Number of mix-servers that take part in the shuffling.
	 * @param group Group of prime order with standard generator g.
	 * @param directory Path to the default directory.
	 * @param verbose Flag that indicates if messages printing is on.
	 * @return
	 */
	public static VerifyKeyResult verifierOfKeys(int lambda, Group group, File directory, boolean verbose)  {
		
		if (verbose)
			System.out.println("Keys Verifying started.");
		
		File proofsDir = new File(directory, "proofs");
		
		/* Joint public key */
		ElementProductGroup pk;
		Group doubleGq;
		try {
			Group[] doubleGqArray = new Group[2];
			doubleGqArray[0] = group;
			doubleGqArray[1] = group;		
			doubleGq = new ProductGroup(doubleGqArray);
			
			File pkFile = new File (directory, "FullPublicKey.bt");
			pk = (ElementProductGroup) doubleGq.createElement(new ByteTree(GeneralUtil.fileToByteArray(pkFile)));
		} catch(Exception e) {
			System.out.println("rejected by the Verifier of Keys\n" + e.getMessage());
			return null;
		}
		
		/* Public keys */
		File ylFile;
		Field Zq;
		ElementProductGroup yl;
		ElementProductField xl;
		try {
			Zq = new PrimeField(group.getSuperGroupOrder());
			Field[] pfield = new Field[lambda];
			Group[] pgroup =  new Group[lambda];
			for (int i = 0; i < lambda; i++)
			{
				pfield[i] = Zq;
				pgroup[i] = group;
			}
			yl = new ElementProductGroup(new ProductGroup(pgroup));
			xl = new ElementProductField(new ProductField(pfield));
		} catch(Exception e) {
			System.out.println("Verifier of Keys failed:\n" + e.getMessage());
			return null;
		}
		
		try
		{
			for (int l = 0; l < lambda; l++)
			{
				ylFile = new File(proofsDir, "PublicKey" + GeneralUtil.toDecimalString(l+1) + ".bt");
				yl.set(l, group.createElement(new ByteTree(GeneralUtil.fileToByteArray(ylFile))));			
			}
		} catch (Exception e) {
			System.out.println("rejected by the Verifier of Keys\n" + e.getMessage());
			return null;
		}
		
		try
		{
			ElementGroup ylmult = yl.get(0);
			for (int l = 1; l < lambda; l++)
			{
				ylmult = ylmult.multiply(yl.get(l));			
			}
			
			if (!pk.get(1).equals(ylmult))
			{
				System.out.println("rejected by the Verifier of Keys\n");
				return null;
			}
		} catch (Exception e) {
			System.out.println("Verifier of Keys failed:\n" + e.getMessage());
			return null;
		}
		
		/* Secret keys */
		/* (a) */
		File xlFile;
		
		try
		{
			for (int l = 0; l < lambda; l++)
			{
				xlFile = new File(proofsDir, "SecretKey" + GeneralUtil.toDecimalString(l+1) + ".bt");
				if (!xlFile.exists())
					xl.set(l, null);
				else
					xl.set(l, Zq.createElement(new ByteTree(GeneralUtil.fileToByteArray(xlFile))));
					
					/* (b) */
					if ((xl.get(l) != null) && (!yl.get(l).equals(group.getGroupGenerator().power(xl.getElementValue()))))
					{
						System.out.println("rejected by the Verifier of Keys\n");
						return null;
					}
			}
		} catch (Exception e) {
			System.out.println("rejected by the Verifier of Keys");
			return null;
		}
		
		VerifyKeyResult ret = new VerifyKeyResult(pk, yl, xl);
		
		if (verbose)
			System.out.println("Keys Verifying ended.");
		return ret;
				
	}
	
	/**
	 * This class represents the returned object from the verifierOfKeys function. 
	 * @author Hadar Miller and Or Stern
	 *
	 */
	public static class VerifyKeyResult 
	{
		private ElementProductGroup pk;
		private ElementProductGroup y;
		private ElementProductField x;
		
		public VerifyKeyResult(ElementProductGroup pk, ElementProductGroup y, ElementProductField x)
		{
			this.pk = pk;
			this.y = y;
			this.x = x;
		}
		
		/**
		 * 
		 * @return El Gamal public key.
		 */
		public ElementProductGroup getPK()
		{
			return this.pk;
		}
		
		/**
		 * 
		 * @return partial public key.
		 */
		public ElementProductGroup getY()
		{
			return this.y;
		}
		
		/**
		 * 
		 * @return secret key.
		 */
		public ElementProductField getX()
		{
			return this.x;
		}
	}
	
	
	
}
